package com.coupon.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.api.client.IdClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.constant.RedisKey;
import com.common.constant.UserCouponStatus;
import com.common.context.UserContext;
import com.common.domain.dto.UserCouponAddDTO;
import com.common.domain.po.Coupon;
import com.common.domain.po.UserCoupon;
import com.common.domain.vo.UserCouponListByStatusVO;
import com.common.exception.CodeAndMsg;
import com.common.exception.JavaSmException;
import com.coupon.mapper.CouponMapper;
import com.coupon.mapper.UserCouponMapper;
import com.coupon.service.UserCouponService;
import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;

import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;


/**
 * @author 路亚
 * @version 0.1
 * @className UserCouponServiceImpl
 * @description
 * @date 2024/12/22 17:02
 * @since jdk11
 */
@Service
@RequiredArgsConstructor
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements UserCouponService {

    private final StringRedisTemplate stringRedisTemplate;

    private final DefaultMQProducer defaultMQProducer;

    private final CouponMapper couponMapper;

    private final IdClient idClient;

    private static final RedisScript<Long> RECEIVE_COUPON_SCRIPT;

    static {
        RECEIVE_COUPON_SCRIPT = RedisScript.of(new ClassPathResource("lua/receive_coupon.lua"), Long.class);
    }

    @Override
    public void getCoupon(Long couponId) {
        /**
         * 根据id查询优惠券、查询用户已领取的数量、新增UserCoupon记录、更新coupon表
         * 领取优惠券是高并发操作，这样会对数据库产生较大的压力，所以要使用 redis
         * 但仍然还有 新增UserCoupon记录、更新coupon 等需要操作数据库
         * 所以为了提高效率采用最终异步写策略，以上流程可以优化
         */
        // 使用lua脚本操作此接口
        String key = RedisKey.COUPON_TYPE + couponId;
        String userCouponKey = RedisKey.USER_COUPON_KEY;
        Long userId = UserContext.getCurrentId();
        try {
            stringRedisTemplate.execute(RECEIVE_COUPON_SCRIPT,
                    List.of(key, userCouponKey), userId + "", couponId + "");
        }catch (Exception e){
            throw new JavaSmException(CodeAndMsg.COUPON_ERROR);
        }
//        // 判断redis中是否有优惠券
//        Boolean flag = stringRedisTemplate.hasKey(key);
//        // 2.如果存在优惠券，校验是否在发放期
//        if (!flag){
//            throw new JavaSmException(CodeAndMsg.COUPON_NOT_EXIST);
//        }
//        // 获取到结束方法时间距离计算机元年的毫秒值
//        Long issueEndTime = Long.parseLong(stringRedisTemplate.opsForHash().get(key, "issueEndTime").toString());
//        // 根据当前时间和结束发放时间判断是否在发放期
//        if (System.currentTimeMillis() > issueEndTime){
//            throw new JavaSmException(CodeAndMsg.COUPON_NOT_ISSUE);
//        }
//        // 4.校验库存
//        if (Long.parseLong(stringRedisTemplate.opsForHash().get(key, "totalNum").toString()) <= 0){
//            throw new JavaSmException(CodeAndMsg.COUPON_NOT_ENOUGH);
//        }
//        // 3.校验用户是否超领
//        // 3.1 获取当前用户的id，用于从redis里获取当前用户已领取的数量
//        Long userId = UserContext.getCurrentId();
//        Object obj = stringRedisTemplate.opsForHash().get(RedisKey.USER_COUPON_KEY, userId + "," + couponId);
//        // 3.2如果redis中不存在当前用户，说明用户是第一次领取优惠卷
//        // 向redis中新增当前用户，key是用户id，value默认是1
//        if (!ObjectUtil.isNotNull(obj)){
//            // 如果是空，说明用户是第一次领取优惠卷，那么直接插入redis即可
//            stringRedisTemplate.opsForHash().put(RedisKey.USER_COUPON_KEY, userId + "," +couponId, 1 + "");
//            // 还需要给优惠卷库存减一
//            stringRedisTemplate.opsForHash().put(key, "totalNum", Long.parseLong(stringRedisTemplate.opsForHash().get(key, "totalNum").toString()) - 1 + "");
//        }else {
//            // 如果不是空，那么需要判断当前用户是否超过领取数量
//            // 先获取当前用户已领取的数量
//            Long count = Long.parseLong(obj.toString());
//            // 获取每个用户可以领取的数量
//            Long userLimit = Long.parseLong(stringRedisTemplate.opsForHash().get(key, "userLimit").toString());
//            if (count >= userLimit){
//                throw new JavaSmException(CodeAndMsg.COUPON_LIMIT);
//            }
//            // 如果不超过领取数量，那么就给当前用户新增一个领取数量
//            stringRedisTemplate.opsForHash().put(RedisKey.USER_COUPON_KEY, userId + "," + couponId, count + 1 + "");
//            // 还需要给优惠卷库存减一
//            stringRedisTemplate.opsForHash().put(key, "totalNum", Long.parseLong(stringRedisTemplate.opsForHash().get(key, "totalNum").toString()) - 1 + "");
//        }
        // 5.发送mq消息，异步更新数据库
        JSONObject json = new JSONObject();
        json.put("couponId", couponId);
        json.put("userId", userId);
        try {
            Message message = new Message(
                    "video",
                    "getCoupon",
                    json.toJSONString().getBytes("utf-8")
            );
            defaultMQProducer.send(message);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void addUserCoupon(UserCouponAddDTO userCouponAddDTO) {
        // 1.更新优惠卷表
        Integer r = couponMapper.incrIssueNum(userCouponAddDTO.getCouponId());
        if (r == 0){
            throw new JavaSmException(CodeAndMsg.COUPON_NOT_ENOUGH);
        }
        // 2.新增用户优惠卷数据
        Coupon coupon = couponMapper.selectById(userCouponAddDTO.getCouponId());
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setId(idClient.getLongKeyInterval("user_coupon"));
        userCoupon.setUserId(userCouponAddDTO.getUserId());
        userCoupon.setCouponId(userCouponAddDTO.getCouponId());
        userCoupon.setTermBeginTime(coupon.getTermBeginTime());
        userCoupon.setTermEndTime(coupon.getTermEndTime());
        userCoupon.setStatus(UserCouponStatus.UNUSED);
        userCoupon.setCreateTime(LocalDateTime.now());
        userCoupon.setUpdateTime(LocalDateTime.now());
        save(userCoupon);
    }

    @Override
    public UserCouponListByStatusVO listByStatus(Integer status, Integer page) {
        Page<UserCoupon> userCouponPage = new Page<>(page, 10);
        QueryWrapper<UserCoupon> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", UserContext.getCurrentId());
        if (status != null){
            queryWrapper.eq("status", status);
        }
        Page<UserCoupon> resultPage = page(userCouponPage, queryWrapper);
        return new UserCouponListByStatusVO(resultPage.getTotal(), page, resultPage.getRecords());
    }
}
